﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
#endregion

namespace GameBase.Inputs
{
    /// <summary>
    /// Basic functionality needed for gamepad vibration management.    
    /// </summary>
    public class VibrationState
    {
        #region Fields

        readonly Dictionary<PlayerIndex, VibrationSettings> vibrationSettings = new Dictionary<PlayerIndex, VibrationSettings>();

        bool isPaused;

        #endregion

        #region Properties

        /// <summary>
        /// Enables or disables gamepad vibration (true -> enabled; false -> disabled)
        /// </summary>
        public bool Enabled { get; set; }

        #endregion

        #region Singleton Pattern

        // the only instance of this class... eager initialization
        static readonly VibrationState instance = new VibrationState();

        // private constructor...
        // ... creates a new instance of this class
        private VibrationState()
        {
            vibrationSettings.Add(PlayerIndex.One, new VibrationSettings());
            vibrationSettings.Add(PlayerIndex.Two, new VibrationSettings());
            vibrationSettings.Add(PlayerIndex.Three, new VibrationSettings());
            vibrationSettings.Add(PlayerIndex.Four, new VibrationSettings());
        }

        /// <summary>
        /// Gets a common instance of this class.
        /// </summary>
        public static VibrationState Instance
        {
            get { return instance; }
        }

        #endregion

        #region Start, Pause, Resume, Cancel

        /// <summary>
        /// Starts gamepad vibration for a given player.
        /// </summary>
        /// <param name="playerIndex">Player for which set gamepad vibration.</param>
        /// <param name="leftMotor">The speed of the left motor, between 0.0 and 1.0.</param>
        /// <param name="rightMotor">The speed of the right motor, between 0.0 and 1.0.</param>
        /// <param name="duration">The duration of the gamepad vibration.</param>
        public void Start(PlayerIndex playerIndex, float leftMotor, float rightMotor, TimeSpan duration)
        {
            if (isPaused)
                return;

            VibrationSettings settings = vibrationSettings[playerIndex];

            settings.Left = leftMotor;
            settings.Right = rightMotor;
            settings.Duration = duration;
            settings.CurrentTime = TimeSpan.Zero;

            GamePad.SetVibration(playerIndex, leftMotor, rightMotor);
        }

        /// <summary>
        /// Pauses gamepad vibration for all players.
        /// </summary>
        public void Pause()
        {
            isPaused = true;

            for (PlayerIndex playerIndex = PlayerIndex.One; playerIndex <= PlayerIndex.Four; playerIndex++)
            {
                GamePad.SetVibration(playerIndex, 0.0f, 0.0f);
            }
        }

        /// <summary>
        /// Resumes gamepad vibration for all players.
        /// </summary>
        public void Resume()
        {
            isPaused = false;
        }

        /// <summary>
        /// Cancels gamepad vibration for all players.
        /// </summary>
        public void Cancel()
        {
            isPaused = false;

            for (PlayerIndex playerIndex = PlayerIndex.One; playerIndex <= PlayerIndex.Four; playerIndex++)
            {
                VibrationSettings settings = vibrationSettings[playerIndex];
                settings.Duration = TimeSpan.Zero;
                settings.CurrentTime = TimeSpan.Zero;

                GamePad.SetVibration(playerIndex, 0.0f, 0.0f);
            }
        }

        #endregion

        #region Update

        /// <summary>
        /// Called when gamepad vibration needs to be updated.
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to this method.</param>
        public void Update(GameTime gameTime)
        {
            if (isPaused)
                return;

            for (PlayerIndex playerIndex = PlayerIndex.One; playerIndex <= PlayerIndex.Four; playerIndex++)
            {
                VibrationSettings settings = instance.vibrationSettings[playerIndex];

                if (settings.CurrentTime < settings.Duration)
                {
                    settings.CurrentTime += gameTime.ElapsedGameTime;
                    if (settings.CurrentTime > settings.Duration)
                    {
                        settings.CurrentTime = settings.Duration;
                        GamePad.SetVibration(playerIndex, 0.0f, 0.0f);
                        continue;
                    }

                    float progress = (float)(settings.CurrentTime.TotalMilliseconds / settings.Duration.TotalMilliseconds);

                    float left = settings.Left * (1.0f - progress);
                    float right = settings.Right * (1.0f - progress);

                    if (Enabled)
                        GamePad.SetVibration(playerIndex, left, right);
                    else
                        GamePad.SetVibration(playerIndex, 0.0f, 0.0f);
                }
            }
        }

        #endregion
    }

    #region VibrationSettings

    /// <summary>
    /// Stores gamepad vibration settings for a given player.
    /// </summary>
    internal class VibrationSettings
    {
        #region Properties

        /// <summary>
        /// Gets or sets the speed of the left motor, between 0.0 and 1.0. 
        /// </summary>
        public float Left { get; set; }

        /// <summary>
        /// Gets or sets the speed of the right motor, between 0.0 and 1.0.
        /// </summary>
        public float Right { get; set; }

        /// <summary>
        /// Gets or sets the duration of the gamepad vibration.
        /// </summary>
        public TimeSpan Duration { get; set; }

        /// <summary>
        /// Gets or sets the time the gamepad vibration has been active.
        /// </summary>
        public TimeSpan CurrentTime { get; set; }

        #endregion
    }

    #endregion
}
